home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1993…ch: Other People's Memory / ADC Developer CD (1993-03) (''Other People's Memory'')_iso / Dev.CD Mar 93.iso / Development Platforms / Apple II / Essentials / Technical.Notes / IIGS / TN.IIGS.071 < prev    next >
Encoding:
Text File  |  1992-07-15  |  14.9 KB  |  341 lines  |  [TEXT/GEOL]

  1. Apple II
  2. Technical Notes
  3. _____________________________________________________________________________
  4.                                                   Developer Technical Support
  5. Apple IIgs
  6. #71: DA Tips and Techniques
  7.  
  8. Revised by: Dave "Mr. Tangent" Lyons                                 May 1992
  9. Written by: Dave Lyons                                          November 1989
  10.  
  11. This Technical Note presents tips and techniques for writing Desk Accessories.
  12.  
  13. CHANGES SINCE DECEMBER 1991:  Reworked discussion of NDAs and Command-
  14. keystrokes.  Marked obsolete steps in "NDAs Can Have Resource Forks."
  15. _____________________________________________________________________________
  16.  
  17.  
  18. CLASSIC DESK ACCESSORY TIPS AND TECHNIQUES
  19.  
  20. READING THE KEYBOARD
  21.  
  22. For a CDA that runs only under GS/OS, the Console Driver is the best choice
  23. for reading from the keyboard.  Other CDAs have two cases to deal with:  the
  24. Event Manager may or may not be started.  The Text Tools can read the keyboard
  25. in either case, but you should avoid using the Text Tools whenever possible
  26. (see Apple IIgs Technical Note #69, The Ins and Outs of Slot Arbitration).
  27.  
  28. You can call EMStatus to determine whether the Event Manager is started.  When
  29. it is, you can read keypresses by calling GetNextEvent.  When the Event
  30. Manager is not started, you can read keys directly from the keyboard hardware
  31. by waiting for bit 7 of location $E0C000 to turn on.  When it does, the lower
  32. seven bits represent the key pressed.  Once you've detected a keypress, you
  33. need to write to location $E0C010 to remove the keypress from the buffer.
  34.  
  35. Alternately, you can use IntSource (in the Miscellaneous Tools) to temporarily
  36. disable keyboard interrupts and then read the keyboard hardware directly.  Be
  37. sure to reactivate keyboard interrupts if, and only if, they were previously
  38. enabled.
  39.  
  40. JUST ONE PAGE OF STACK SPACE
  41.  
  42. CDAs normally have only a single page of stack space available to them (256
  43. bytes at $00/01xx).  Your CDA may or may not be able to allocate additional
  44. stack space from bank 0 during execution.  The following code (written for the
  45. MPW IIgs cross-assembler) shows a safe way to try to allocate more stack space
  46. and to switch between stacks when the space is available.
  47.  
  48. If ProDOS 8 is active, your CDA cannot allocate additional space (and there is
  49. no completely safe way to "borrow" bank 0 space from the ProDOS 8
  50. application).
  51.  
  52. HowMuchStack   gequ    $1000          ;try for 4K of stack space
  53.  
  54. start          phd
  55.                phb
  56.                phk
  57.                plb
  58.                pha                    ;Space for result
  59.                pha
  60.                PushLong #HowMuchStack
  61.                pha
  62.                _MMStartUp
  63.                pla
  64.                ora    #$0f00          ;OR in an arbitrary auxiliary ID
  65.                pha
  66.                PushWord #$C001        ;fixed, locked, use specified bank
  67.                PushLong #0            ;(specify bank 0)
  68.                _NewHandle
  69.                tsc
  70.                sta    theOldStack
  71.                bcs    NoStackSpace    ;still set from _NewHandle
  72.                tcd
  73.                lda    [1]
  74.                tcd
  75. ;              clc                    ;carry is already clear
  76.                adc    #HowMuchStack-1
  77. NoStackSpace   pha
  78.                ldx    #$fe
  79. keepStack      lda    >$000100,x
  80.                sta    stackImage,x
  81.                dex
  82.                dex
  83.                bpl    keepStack
  84.                pla
  85.                tcs
  86.                jsl    RealCDAentry     ;carry is clear if large stack
  87.                                        ;available
  88.                php
  89.                php
  90.                pla
  91.                sta    pRegister
  92.                sei
  93.                ldx    #$fe
  94. restoreStack   lda    stackImage,x
  95.                sta    >$000100,x
  96.                dex
  97.                dex
  98.                bpl    restoreStack
  99.                lda    theOldStack
  100.                tcs
  101.                lda    pRegister
  102.                pha
  103.                plp
  104.                plp
  105.                lda    1,s
  106.                ora    3,s
  107.                beq    noDispose
  108.                _DisposeHandle
  109.                bra    Exit
  110. noDispose      pla
  111.                pla
  112. Exit           plb
  113.                pld
  114.                rtl
  115. pRegister     ds 2
  116. theOldStack   ds 2
  117. stackImage    ds.b 256
  118.  
  119.  
  120. When this routine calls RealCDAentry, the carry flag is set if no extra stack
  121. space is available.  If the carry is clear, the additional stack space was
  122. available and the direct-page register points to the bottom of that space.
  123.  
  124.  
  125. RealCDAentry   bcs    smallStack            ;if c set, only 1 page of stack
  126.                                             ;is available
  127.                ...                          ; put something interesting here
  128.                rtl
  129.  
  130. smallStack     _SysBeep
  131.                rtl
  132.  
  133.  
  134. Note that interrupts are disabled while the page-one stack is being restored;
  135. they are reenabled (if they were originally enabled) only after the stack
  136. pointer is safely back in page one.
  137.  
  138. INTERRUPTS, EVENT MANAGER, MEMORY, AND CDAS
  139.  
  140. Whether the Event Manager is active or not, the user hits Apple-Ctrl-Esc and
  141. usually gets to the CDA menu.  It looks the same, but what happens internally
  142. is different affects what happens when your CDA allocates memory.
  143.  
  144. When the Event Manager is active (as it normally is while the user is running
  145. a Desktop application), hitting Apple-Ctrl-Esc posts a deskAcc event to the
  146. event queue.  The CDA menu appears only when the application calls
  147. GetNextEvent or EventAvail with the deskAcc bit enabled in the event mask.
  148.  
  149. So with the Event Manager active, the CDA menu and individual CDAs are running
  150. in the "foreground"--no processor interrupt is being serviced, and the
  151. foreground application is stuck inside the GetNextEvent or EventAvail call.
  152. The Memory Manager knows that no interrupt is in progress, so it will happily
  153. compact and purge memory if necessary to carry out a memory allocation request
  154. from your CDA.  This is just fine, since the foreground application made a
  155. toolbox call--unlocked memory blocks are not guaranteed to stay put.
  156.  
  157. When the Event Manager is not active, hitting Apple-Ctrl-Esc either enters the
  158. CDA menu immediately (if the system Busy Flag is zero) or calls SchAddTask so
  159. that the CDA menu appears during a the next DECBUSYFLG call that brings the
  160. system Busy Flag down to zero.  If the CDA menu appears during a DECBUSYFLG ,
  161. normal memory compaction and purging are possible, just like when the Event
  162. Manager is active.
  163.  
  164. But if the Busy Flag was zero when the user hit Apple-Ctrl-Esc, then the CDA
  165. menu appears inside of the interrupt, and the foreground application is at an
  166. unknown point where it may justifiably expect that unlocked memory blocks will
  167. not move or be purged (see Apple IIgs Toolbox Reference, Volume 1, page 12-5).
  168. (Note that the Desk Manager does a tricky dance to allow additional interrupts
  169. to occur, even though the Apple-Ctrl-Esc interrupt will not return until the
  170. user chooses Quit from the CDA menu.  Normally interrupts cannot be nested;
  171. the Desk Manager and AppleTalk are exceptions.)
  172.  
  173. The Memory Manager knows an interrupt is in progress, so CompactMem takes no
  174. action and memory allocation requests do not cause unlocked memory blocks to
  175. move and do not attempt to purge purgeable blocks to make room.  Memory
  176. allocation requests will still normally succeed, but you will not be able to
  177. allocate a block larger than the value returned by MaxBlock.
  178. New Desk Accessory Tips and Techniques
  179.  
  180. AN NDA CAN FIND ITS MENU ITEM ID
  181.  
  182. After the application has called FixAppleMenu, an NDA can look at its menu
  183. item template (after the "\H" in the NDA header) to determine the menu ID
  184. corresponding to the NDA's name in the Apple menu.  This is sometimes useful
  185. to pass to OpenNDA (if the NDA has some way to open itself), or to pass to a
  186. Menu Manager call.
  187.  
  188. Finding the menu item ID in the NDA's header is easy if the NDA is written in
  189. assembly.  In a high-level language it may be harder (if you don't have direct
  190. access to your NDA's header, you need to find it on the fly and scan for the
  191. "\H").
  192.  
  193. NDAS AND COMMAND- KEYSTROKES
  194.  
  195. To give the user a consistent way to close NDA windows, System 6.0 handles
  196. Command-W automatically when a system window is in front.  It calls
  197. CloseNDAbyWinPtr without letting the NDA or the application see the Command-W.
  198.  
  199. However, there is a special action code (optionalCloseAction) that an NDA can
  200. accept to handle the Close request itself.  This way the NDA can offer the
  201. user a chance to cancel the Close, which is impossible when the system calls
  202. the NDA's main Close routine, as CloseNDAByWinPtr does.  (See the System 6.0
  203. Toolbox documentation for details.)
  204.  
  205. There is no way for an NDA to accept some keystrokes and pass others along to
  206. applications, but if your NDA does not want any keystroke events, turn off the
  207. corresponding eventMask bits in the NDA header (this allows the application to
  208. receive keystrokes while your NDA window is in front).
  209.  
  210. CALLING INSTALLNDA FROM WITHIN AN NDA
  211.  
  212. It is possible to write an NDA that installs other NDAs.  However, with System
  213. Software 5.0 and later, InstallNDA returns an error when called from an NDA.
  214. When your NDA has control because the Desk Manager called one of your NDA's
  215. entry points, the Desk Manager's data structures are already in use, so
  216. InstallNDA is unable to modify them.
  217.  
  218. The solution is to use SchAddTask in the Scheduler to postpone the InstallNDA
  219. call until the system is not busy.  Remember that the Bank and Direct Page
  220. registers are not defined when your scheduled task is executed.
  221.  
  222. PROCESSING MOUSEUP EVENTS
  223.  
  224. When an NDA's action routine receives a mouseUp event, it is not always safe
  225. for the NDA to draw in its window.
  226.  
  227. For example, when the user drags an NDA window, the NDA receives the mouseUp
  228. before the window is actually moved, and before DragWindow erases the outline
  229. of the new window position, which may overlap the window's content.  In
  230. addition, when the user chooses a menu item, the front NDA receives the
  231. mouseUp before the menu's image is removed, and the image may overlap the
  232. NDA's window.  In either case, drawing in the window makes a mess.
  233.  
  234. The solution is to avoid drawing in direct response to a mouseUp.  Instead,
  235. invalidate part of the window to force an update event to happen later.
  236. NDAs Can Have Resource Forks
  237.  
  238. Following is the recommended way for a New Desk Accessory to use its file's
  239. resource fork.
  240.  
  241. In the NDA's Open routine, do the following.  Steps that are obsolete (and
  242. safely omitted) with System Software 6.0 and later are marked with an asterisk
  243. (*):
  244.  
  245.    1. Call GetCurResourceApp and keep the result.
  246.    2. If the NDA does not already know its Memory Manager user ID, call
  247.       MMStartUp to get it.
  248.    3. Call ResourceStartUp using the NDA's user ID.
  249.    4. Call the Loader function LGetPathname2 with the NDA's user ID (and a
  250.       fileNumber of $0001) to get a pointer to the NDA's pathname.  (The
  251.       result is a pointer to a class-one GS/OS string.)
  252.   *5. Use GetLevel to get the current file level, then use SetLevel to set it
  253.       to zero.  This helps protect your resource fork from being closed
  254.       accidentally.
  255.    6. Use GetSysPrefs to get the current OS preferences, then use SetSysPrefs
  256.       to ensure that the user is prompted, if necessary, to insert the disk
  257.       containing your resource fork.  (To compute the new preferences word,
  258.       take the current one, AND it with $1FFF, and ORA it with $8000.  This
  259.       tells GS/OS to deal with volume-not-found conditions by putting up a
  260.       please-insert-disk dialog with an OK button and a Cancel button.)
  261.    7. Call OpenResourceFile using the result from LGetPathname2.  Save the
  262.       returned fileID--you need it when closing the file.  (Be prepared to
  263.       deal with an error, such as $0045, Volume Not Found.)
  264.    8. Use SetSysPrefs to restore the OS preferences saved in step six.
  265.   *9. Use SetLevel to restore the file level to its old value (saved in step
  266.       five).
  267.   10. Call SetCurResourceApp with the old value saved in step one.
  268.  
  269. In the NDA's action routine, no special calls are necessary--the Desk Manager
  270. calls SetCurResourceApp automatically before calling your action routine, so
  271. your NDA's own resource search path is already in effect.
  272.  
  273. Run queue routines and NDA installs with AddToRunQ are treated the same
  274. way--the NDA's resource search path is automatically in effect when the run
  275. queue routine is called.
  276.  
  277. In the NDA's Close routine, do the following:
  278.  
  279.    1. Call CloseResourceFile with the fileID that was returned when you
  280.       opened it.
  281.    2. Call ResourceShutDown with no parameters.
  282.  
  283.  
  284. NDAS MUST BE CAREFUL HANDLING MODAL WINDOWS
  285.  
  286. If your NDA uses its resource fork and calls TaskMaster with a restricted
  287. wmTaskMask to produce a modal window, you must be careful not to allow
  288. TaskMaster to update the contents of any application windows that happen to
  289. need updating.
  290.  
  291. The problem is that an application window's wContDraw routine can reasonably
  292. assume that the current Resource Manager search path is the application's, but
  293. TaskMaster does not take any special steps to set it.  When the content-draw
  294. routine draws controls which were created from resources which are not
  295. presently in the resource search path, the system may crash.
  296.  
  297. If your NDA does not start up the Resource Manager, the Desk Manager is unable
  298. to SetCurResourceApp to your NDA, so the application's search path is still in
  299. effect--no problem.  But if your NDA does start the Resource Manager, you have
  300. to be careful not to cause application routines to be called.
  301.  
  302. AVOID HARD-CODING YOUR PATHNAME
  303.  
  304. If your NDA needs to know its own pathname or the pathname of the directory
  305. it's in, call LGetPathname or LGetPathname2 using your User ID.  This is a
  306. better method than hard-coding "*:System:Desk.Accs:MyDAName" because the user
  307. may change your DA's file name or use a utility to install it from some
  308. non-standard directory.
  309.  
  310. AVOID EXTRA GETNEWID CALLS
  311.  
  312. Normally there is no reason for a Desk Accessory to call GetNewID.  When you
  313. can, just call MMStartUp to find your own User ID, and use that.  You can
  314. freely use all the auxiliary IDs derived from your main ID (MMStartUp+$0100,
  315. MMStartUp+$0200, ..., MMStartUp+$0F00).
  316.  
  317. By not calling GetNewID, you conserve the limited supply of IDs (255 of in the
  318. $50xx range for Desk Accessories), and you make life easier for people trying
  319. to debug their systems, since all your allocated memory can be readily
  320. identified.
  321.  
  322. OPEN IS NOT CALLED IF NDA IS ALREADY OPEN
  323.  
  324. Your NDA's Open routine does not get called if the user chooses the NDA from
  325. the Apple menu while the NDA is already open.  In this case, the Desk Manager
  326. simply calls SelectWindow on your existing window.
  327.  
  328. There is no need to include code in your Open routine to check if your window
  329. is already open, and to call SelectWindow if it is.
  330.  
  331.  
  332. Further Reference
  333. _____________________________________________________________________________
  334.  
  335.    o   Apple IIgs Toolbox Reference, Volumes 1-3
  336.    o   GS/OS Reference
  337.    o   Apple IIgs Hardware Reference
  338.    o   Apple IIgs Technical Note #53, Desk Accessories and Tools
  339.    o   Apple IIgs Technical Note #57, The Memory Manager and Interrupts
  340.    o   Apple IIgs Technical Note #69, The Ins and Outs of Slot Arbitration
  341.